
package com.probiz.estore.core.security;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.ConfigAttributeEditor;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.intercept.web.AbstractFilterInvocationDefinitionSource;
import org.acegisecurity.intercept.web.FilterInvocationDefinition;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;

import com.probiz.estore.Constants;
import com.probiz.estore.common.model.system.AppRole;
import com.probiz.estore.core.util.CacheUtil;
import com.probiz.estore.core.util.StringUtil;
import com.probiz.estore.system.service.AppRoleManager;
import com.probiz.estore.webapp.util.RequestContext;

import edu.emory.mathcs.backport.java.util.Arrays;

/**
 * 授权资源
 * 
 */
public class UrlRegExpBasedFilterInvocationDefinitionMap extends	AbstractFilterInvocationDefinitionSource implements	InitializingBean,FilterInvocationDefinition 
{
	private static final Log logger	= LogFactory.getLog(UrlRegExpBasedFilterInvocationDefinitionMap.class);
	String	STAND_DELIM_CHARACTER	= ",";
	private AppRoleManager appRoleManager;
	private Cache urlResourcesCache;
	private ResourceMappingProvider	resourceMappingProvider;
    private boolean convertUrlToLowercaseBeforeComparison = false;
    /**
	 * 高优先级公用url,所有角色都有授权
	 */
	private List<String> hightPriorityPublicURLs	= new ArrayList<String>();
	
	/**
	 * 低优先级公用url,所有角色都有授权
	 */
	private List<String> lowPriorityPublicURLs	= new ArrayList<String>();
	
	public void setAppRoleManager(AppRoleManager appRoleManager) {
		this.appRoleManager = appRoleManager;
	}
	

	public void setUrlResourcesCache(Cache urlResourcesCache) {
		this.urlResourcesCache = urlResourcesCache;
	}

	
	public void setHightPriorityPublicURLs(List<String> hightPriorityPublicURLs) {
		this.hightPriorityPublicURLs = hightPriorityPublicURLs;
	}


	public void setLowPriorityPublicURLs(List<String> lowPriorityPublicURLs) {
		this.lowPriorityPublicURLs = lowPriorityPublicURLs;
	}


	public void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison) {
		this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
	}
	public boolean isConvertUrlToLowercaseBeforeComparison() {
		return convertUrlToLowercaseBeforeComparison;
	}

	public void setResourceMappingProvider(ResourceMappingProvider resourceMappingProvider) {
		this.resourceMappingProvider = resourceMappingProvider;
	}
	
	public void afterPropertiesSet() throws Exception {
	}
    
    /**
	 * url资源已被缓存，通过EventUtil在修改了权限资源后清空缓存，重新获取
	 */
    public List<EntryHolder> getUrlResources(){
    	List<EntryHolder> urlResources; 
        Element element = urlResourcesCache.get(CacheUtil.URL_RESOURCES_CACHE_OKEY);
        if (element != null){
        	urlResources = (List<EntryHolder>) element.getObjectValue();
        }else{
        	urlResources = new ArrayList<EntryHolder>();
        	/* add the public urls */
    		List<AppRole> roleList = appRoleManager.findAppRoles(RequestContext.getSecurityBelongTo());
    		Set<String> allRoleNames = new HashSet<String>();
    		for(AppRole role : roleList) {
    			allRoleNames.add(role.getAuthority());
    		}
    		String allRoleNameString = StringUtil.arrayToString(allRoleNames.toArray(new String[0]), STAND_DELIM_CHARACTER);
    		// 所有用户都有hightPriorityPublicURLs的权限
    		for (int j = 0; j < hightPriorityPublicURLs.size(); j++)
    		{
    			addSecureUrl(urlResources, hightPriorityPublicURLs.get(j), allRoleNameString, null);
    		}
    		ResourceMapping[] mappings = resourceMappingProvider.getResourceMappings();
    		
    		if (mappings!=null){
    			Arrays.sort(mappings, new Comparator<ResourceMapping>(){
    										public int compare(ResourceMapping rm1, ResourceMapping rm2){
    											if(rm1.getType()!=null && rm2.getType()!=null && !rm1.getType().equals(rm2.getType())){
    												// 将资源放于菜单前面
    												if(rm1.getType().equals(Constants.SECURITY_TYPE_RESOURCE)){
    													return -1;
    												}else{
    													return 1;
    												}
    											}else{
    												// 其余排序URL，最长的放前面，以达到权限的精确控制
    												if(rm1.getResourcePath().length() > rm2.getResourcePath().length()){
    													return -1;
    												}else if(rm1.getResourcePath().length() < rm2.getResourcePath().length()){
    													return 1;
    												}else{
    													return 0;
    												}
    											}
    										}
    									}
    			);
    			
    			for (int i = 0; i < mappings.length; i++) {
    				// add the role
    				ResourceMapping mapping = mappings[i];
    				Set<GrantedAuthority> recipents = mapping.getRecipients();
    				Set<String> grantedRoleNames = new HashSet<String>();
    				for (GrantedAuthority recipent : recipents) {
    					grantedRoleNames.add(recipent.getAuthority());
    				}
    				if(!RequestContext.isFront()){
    					// 超级管理员具有所有权限
    					grantedRoleNames.add(Constants.DEFAULT_ADMIN_ROLE_NAME);
    				}
    				String value = StringUtil.arrayToString(grantedRoleNames.toArray(new String[0]), STAND_DELIM_CHARACTER);
    					
    				// add this url and role to combine the url security object.
    				if(StringUtils.isNotBlank(value)){
    					if(mapping.getResourcePath().indexOf("修改这里")!=-1){
    						 //-------调试点---
    						 logger.info("Mapping Resource"+value);
    					 }
    					addSecureUrl(urlResources, mapping.getResourcePath(), value, mapping.getType());
    				}
    			}
    		}
    		// 所有用户都有lowPriorityPublicURLs的权限
    		for (int j = 0; j < lowPriorityPublicURLs.size(); j++)
    		{
    			addSecureUrl(urlResources, lowPriorityPublicURLs.get(j), allRoleNameString, null);
    		}
    		// 添加给超级管理员所有权限，也能防止因为未配置权限能进入后台的情况
    		addSecureUrl(urlResources, "/**", Constants.DEFAULT_ADMIN_ROLE_NAME,null);
    		//放入数据缓存中
    		element = new Element(CacheUtil.URL_RESOURCES_CACHE_OKEY, urlResources);
    		urlResourcesCache.put(element);
        }	
        return urlResources;
    }
	
	public Iterator getConfigAttributeDefinitions() {
		Set set = new HashSet();
        Iterator iter = this.getUrlResources().iterator();
        while (iter.hasNext()) {
            EntryHolder entryHolder = (EntryHolder) iter.next();
            set.add(entryHolder.getConfigAttributeDefinition());
        }
        return set.iterator();
	}
	
	private void addSecureUrl(List<EntryHolder> urlResources, String expression, String value, Short type)	throws IllegalArgumentException {
		// Convert value to series of security configuration attributes
		ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor();
		configAttribEd.setAsText(value);
		ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd.getValue();
		if(type != null){
			urlResources.add(new EntryHolder(expression, attr, type));
		}else{
			urlResources.add(new EntryHolder(expression, attr, null));
		}
	}
	
	public void addSecureUrl(String expression, ConfigAttributeDefinition attr) {
		// DO NOTHING JUST IMPLEMENT
	}

	public ConfigAttributeDefinition lookupAttributes(String url) {
		Iterator iter = this.getUrlResources().iterator();

		if (this.convertUrlToLowercaseBeforeComparison) {
			url = url.toLowerCase();
		}
		
		while (iter.hasNext()) {
			EntryHolder entryHolder = (EntryHolder) iter.next();
			if(entryHolder.getUrlPattern().indexOf("修改这里")!=-1){
				 //-------调试点---
				 logger.info("Lookup Resource");
			 }
			boolean matched = UrlMatcher.matches(url, entryHolder.getUrlPattern());
			if (matched) {
				return entryHolder.getConfigAttributeDefinition();
			}
		}

		return null;
	}
	/**
	 * 根据url获得菜单授权情况
	 * 
	 * @param url
	 * @return
	 */
	public ConfigAttributeDefinition lookupAttributes4Menu(String url) {
		Iterator iter = this.getUrlResources().iterator();
		if (this.convertUrlToLowercaseBeforeComparison) {
			url = url.toLowerCase();
		}
		
		while (iter.hasNext()) {
			EntryHolder entryHolder = (EntryHolder) iter.next();
			 if(entryHolder.getUrlPattern().indexOf("修改这里")!=-1){
				 //-------调试点---
				 logger.info("Lookup Menu");
			 }
			if(null != entryHolder.getType() && !entryHolder.getType().equals(Constants.SECURITY_TYPE_MENU)){
				// 如果是非菜单权限，则跳过
				continue;
			} 
			boolean matched = UrlMatcher.matches(url, entryHolder.getUrlPattern());
			if (matched) {
				return entryHolder.getConfigAttributeDefinition();
			}
		}

		return null;
	}


	
	protected class EntryHolder {
		private ConfigAttributeDefinition	configAttributeDefinition;
		private String						urlPattern;
		/**
		 * 权限类型，参看Constants.SECURITY_TYPE_MENU 和
		 * Constants.SECURITY_TYPE_RESOURCE
		 */
		private Short                       type;

		protected EntryHolder() {
			throw new IllegalArgumentException("Cannot use default constructor");
		}

		public EntryHolder(String urlPattern, ConfigAttributeDefinition attr, Short type) {
			this.urlPattern = urlPattern;
			this.configAttributeDefinition = attr;
			this.type = type;
		}

		public ConfigAttributeDefinition getConfigAttributeDefinition() {
			return configAttributeDefinition;
		}

		public String getUrlPattern() {
			return urlPattern;
		}

		public Short getType() {
			return type;
		}

		public void setType(Short type) {
			this.type = type;
		}
		
	}


}
